﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.IO;
using System.Xml;

namespace EvoSim
{
	public class Entity : BinarySerializable
	{
		public string EntityName { get; private set; }

		public World World;

		Point _region;
		public Point Region
		{
			get { return _region; }
			set
			{
				World.Regions[Region.X, Region.Y].Remove(this);
				World.Regions[value.X, value.Y].Add(this);

				_region = value;
			}
		}

		double rotation;
		public double Rotation
		{
			get { return rotation; }
			set
			{
				rotation = value;

				if (rotation < 0)
					rotation = Math.PI * 2 + rotation;
				else if (rotation > Math.PI * 2)
					rotation = rotation - Math.PI * 2;
			}
		}

		EntityPosition _position;
		public EntityPosition Position
		{
			get { return _position; }
			set
			{
				_position = value;

				if (World != null)
				{
					if (World.WorldHasEdges)
					{
						if (_position.X < 0)
							_position.X = 0;
						else if (_position.X >= World.Width)
							_position.X = World.Width - 1;

						if (_position.Y < 0)
							_position.Y = 0;
						else if (_position.Y >= World.Height)
							_position.Y = World.Height - 1;
					}
					else
					{
						if (_position.X < 0)
							_position.X = World.Width + _position.X;
						else if (_position.X > World.Width)
							_position.X = _position.X - World.Width;

						if (_position.Y < 0)
							_position.Y = (World.Height - 1) + _position.Y;
						else if (_position.Y >= World.Height)
							_position.Y = _position.Y - World.Height;
					}

					var region = GetRegionPoint(Position.X, Position.Y);

					if (region.X != Region.X || region.Y != Region.Y)
						Region = region;
				}
			}
		}
		public void Move(double dx, double dy)
		{
			var newPos = Position;
			newPos.X += dx;
			newPos.Y += dy;

			Position = newPos;
		}

		public void CheckRegion()
		{
			DeleteFromRegion();

			_region.X = (int)(_position.X / World.RegionSize);
			_region.Y = (int)(_position.Y / World.RegionSize);

			World.Regions[Region.X, Region.Y].Add(this);
		}
		public void DeleteFromRegion()
		{
			for (int x = 0; x < World.RegionLengthX; x++)
			{
				for (int y = 0; y < World.RegionLengthY; y++)
				{
					if (World.Regions[x, y].Contains(this))
						World.Regions[x, y].Remove(this);
				}
			}
		}

		public Point GetRegionPoint(double x, double y)
		{
			var regionPoint = new Point();
			regionPoint.X = (int)(_position.X / World.RegionSize);
			regionPoint.Y = (int)(_position.Y / World.RegionSize);

			return regionPoint;
		}

		public Color EntityColor;
		public virtual int Radius { get; set; }
		public int Age;

		public bool IsDead { get; private set; }

		internal Entity()
			: base("Entity")
		{
		}
		public Entity(string entityName)
			: base("Entity")
		{
			EntityName = entityName;
		}

		public virtual void Update(GameTime gameTime)
		{
			Age += 1;
		}

		public void Kill()
		{
			IsDead = true;
		}

		protected override void WriteInfo(BinaryWriter w)
		{
			base.WriteInfo(w);

			w.Write(GetIntFromString(EntityName));

			World.Save(w);

			w.Write(Rotation);
			w.Write(Position.X);
			w.Write(Position.Y);
			w.Write(EntityColor.R);
			w.Write(EntityColor.G);
			w.Write(EntityColor.B);
			w.Write(EntityColor.A);
			w.Write(Radius);
			w.Write(Age);

			w.Write(IsDead);
		}

		public override void Load(BinaryReader r, uint id)
		{
			base.Load(r, id);

			EntityName = GetStringFromInt(r.ReadUInt32());

			World = BinarySerializable.GetObject<World>(r);

			Rotation = r.ReadDouble();
			var pos = new EntityPosition();
			pos.X = r.ReadDouble();
			pos.Y = r.ReadDouble();
			_position = pos;
			Color color = new Color();
			color.R = r.ReadByte();
			color.G = r.ReadByte();
			color.B = r.ReadByte();
			color.A = r.ReadByte();
			EntityColor = color;
			Radius = r.ReadInt32();
			Age = r.ReadInt32();

			IsDead = r.ReadBoolean();
		}
	}

	public struct EntityPosition
	{
		public double X;
		public double Y;

		public EntityPosition(double x, double y)
		{
			X = x;
			Y = y;
		}
	}
}
